﻿using System;

// 补充了 接口成员的 显式实现

namespace _34._1_csharp_面向对象_event_interface
{
    class Program
    {
        static void Main(string[] args)
        {
            //Test();
            TestGirl();
            //TestDog();
        }

        // 接口中事件名相同，类类型如果采用了非显式的实现，事件揉一起
        public static void Test()
        {
            var teacher = new Teacher();
            var boy = new Boy();

            teacher.OnCookFinished += boy.Eat;
            teacher.Report();
            teacher.OnCookFinished -= boy.Eat;

            Console.WriteLine("====分割=====");

            IEatable2 t2 = new Teacher();
            t2.OnCookFinished += boy.Eat;
            Teacher t22 = t2 as Teacher;
            t22.Report();

            Console.WriteLine("====分割=====");

            IEatable3 t3 = t22;
            t3.OnCookFinished += boy.Eat;
            Teacher t33 = t3 as Teacher;
            t33.Report();

            // 类类型实现两个具有相同事件的接口，只需要实现一个事件即可
            //  对上述两个接口中的事件分别监听，监听到的方法会存在于 同一个事件中 （分别监听会揉成一个）
        }

        // 接口中事件名相同，类类型如果任意一个采用了显式的实现，事件将不再揉一起了
        public static void TestGirl()
        {
            var girl = new Girl();
            var boy = new Boy();

            girl.OnCookFinished += boy.Eat;
            girl.Report();
            girl.OnCookFinished -= boy.Eat;

            Console.WriteLine("====分割=====");

            IEatable2 t2 = girl;
            t2.OnCookFinished += boy.Eat;
            Girl t22 = t2 as Girl;
            t22.Report();
            t2.OnCookFinished -= boy.Eat;

            Console.WriteLine("====分割=====");

            IEatable3 t3 = girl;
            t3.OnCookFinished += boy.Eat;
            Girl t33 = t3 as Girl;
            t33.Report();

        }

        public static void TestDog()
        {
            var boy = new Boy();
            var dog = new Dog();
            //dog.OnCookFinished += boy.Eat; //NG ，接口成员被显式实现，因此不能通过 Dog 的实例访问
            IEatable2 dog_i = dog;
            dog_i.OnCookFinished += boy.Eat;
            dog.Eat();
        }
    }

    // 接口中声明事件, 修饰符只能为 public ，可写可不写
    //  接口中声明之间，修饰符为 internal ，只能显式实现，且类类型的实例无法直接访问(需要配合额外字段)。
    // 接口中能否自己实现事件 : 能，但是形如本例中配合使用的 OnTest2，他自己也会被实现
    interface IEatable
    {
        /// <summary>
        /// 事件：当完成做饭时
        /// <br>cook : 烹饪，蒸煮</br>
        /// <br>finish : 完成 </br>
        /// </summary>
        public event Action OnCookFinished;
        public event Action OnTest;
        event Action OnTest2;
        public event Action OnTest22
        {
            add
            {
                OnTest2 += value;
            }

            remove
            {
                OnTest2 -= value;
            }
        }

        // internal 也可以用在接口，但是可以在类中必须显式实现，
        // 但是好像无法投入使用，因为实现的只是一个空壳子，需要额外写一个事件(委托)的字段与之匹配，形如: Action ac
        // 在 add 访问器下写 : ac+=value，在 remove 访问器下写 ac-=value;
        internal event Action test;

    }

    // 类类型中实现事件
    public class Person : IEatable
    {
        public event Action OnCookFinished;
        public event Action OnTest;
        public event Action OnTest2;

        Action a;
        event Action IEatable.test
        {
            add
            {
                a += value;
            }

            remove
            {
                a -= value;
            }
        }



        public void Invoke() 
        {
            OnCookFinished?.Invoke();
            OnTest?.Invoke();
            a?.Invoke();
        }
    }


    // 两个接口中有相同的事件
    interface IEatable2
    {
        event Action OnCookFinished;
    }
    interface IEatable3
    {
        event Action OnCookFinished;
    }

    public class Teacher : IEatable2, IEatable3
    {
        public event Action OnCookFinished;

        public void Report()
        {
            OnCookFinished?.Invoke();
        }
    }

    public class Boy
    {
        public void Eat()
        {
            Console.WriteLine("吃");
        }
    }

    // 显式实现接口的事件，就是使用 [接口.事件] ,在本例中是：IEatable2.OnCookFinished
    // 显式实现接口的事件，该事件不能有访问修饰符
    // 使用 IDE 小灯泡提示自动完成接口成员的显式实现。
    //  IEatable2 显式实现的接口，不能使用 Dog 实例进行访问, 只能通过接口的实例访问
    public class Dog : IEatable2
    {
        Action action;
        event Action IEatable2.OnCookFinished
        {
            add => action += value;
            remove => action -= value;
        }

        public void Eat()
        {
            action?.Invoke();
        }
    }
}
